package logx

import (
	"context"
	"fmt"
	"os"

	"gitee.com/workits/pkgs/contextx"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var l *zap.Logger

func fieldcov(format string, args ...any) (string, []zap.Field) {
	if len(args) > 0 {
		msgArgs := make([]any, 0)
		fields := make([]zap.Field, 0)

		for _, arg := range args {
			// 处理traceid&uid
			if ctx, ok := arg.(context.Context); ok {
				if traceId := contextx.GetTraceId(ctx); len(traceId) > 0 {
					fields = append(fields, zap.String("traceid", traceId))
				}
				if traceUserId := contextx.GetTraceUserId(ctx); traceUserId != nil {
					fields = append(fields, zap.Any("uid", traceUserId))
				}
				continue
			}
			// 处理自定义字段
			if field, ok := arg.(Field); ok {
				fields = append(fields, zap.Field(field))
				continue
			}
			msgArgs = append(msgArgs, arg)
		}

		return fmt.Sprintf(format, msgArgs...), fields
	}
	return format, []zap.Field{}
}

func getZapLevel(level string) zapcore.Level {
	switch level {
	case "debug":
		return zapcore.DebugLevel
	case "info":
		return zapcore.InfoLevel
	case "warn":
		return zapcore.WarnLevel
	case "error":
		return zapcore.ErrorLevel
	case "panic":
		return zapcore.PanicLevel
	case "fatal":
		return zapcore.FatalLevel
	default:
		return zapcore.InfoLevel
	}
}

func defaultEncoder(isJSON bool) zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.TimeKey = "time"
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	if isJSON {
		return zapcore.NewJSONEncoder(encoderConfig)
	}
	return zapcore.NewConsoleEncoder(encoderConfig)
}

// Init 初始化日志
func Init(cfg Config) {
	var cores []zapcore.Core

	// 控制台日志
	if cfg.EnableConsole {
		level := getZapLevel(cfg.ConsoleLevel)
		writer := zapcore.Lock(os.Stdout)
		core := zapcore.NewCore(defaultEncoder(cfg.ConsoleJsonFormat), writer, level)
		cores = append(cores, core)
	}

	// 文件日志
	if cfg.EnableFile {
		level := getZapLevel(cfg.FileLevel)
		writer := zapcore.AddSync(&lumberjack.Logger{
			Filename: cfg.FileLocation,
			MaxSize:  cfg.MaxSize,
			Compress: cfg.Compress,
			MaxAge:   cfg.MaxAge,
		})
		core := zapcore.NewCore(defaultEncoder(cfg.FileJsonFormat), writer, level)
		cores = append(cores, core)
	}

	// 实例化
	combinedCore := zapcore.NewTee(cores...)
	l = zap.New(combinedCore, zap.AddCaller(), zap.AddCallerSkip(1))
}

func Debugf(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Debug(msg, fields...)
}

func Infof(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Info(msg, fields...)
}

func Warnf(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Warn(msg, fields...)
}

func Errorf(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Error(msg, fields...)
}

func Panicf(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Panic(msg, fields...)
}

func Fatalf(format string, args ...any) {
	msg, fields := fieldcov(format, args...)
	l.Fatal(msg, fields...)
}

type Field zap.Field

func String(key, val string) Field {
	return Field(zap.String(key, val))
}

func Bool(key string, val bool) Field {
	return Field(zap.Bool(key, val))
}

func Int(key string, val int) Field {
	return Field(zap.Int(key, val))
}

func Int8(key string, val int8) Field {
	return Field(zap.Int8(key, val))
}

func Int16(key string, val int16) Field {
	return Field(zap.Int16(key, val))
}

func Int32(key string, val int32) Field {
	return Field(zap.Int32(key, val))
}

func Int64(key string, val int64) Field {
	return Field(zap.Int64(key, val))
}

func Float32(key string, val float32) Field {
	return Field(zap.Float32(key, val))
}

func Float64(key string, val float64) Field {
	return Field(zap.Float64(key, val))
}

func Any(key string, val any) Field {
	return Field(zap.Any(key, val))
}
